package ar.edu.unlp.info.tesis.database.operator;

import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.type.Type;

import ar.edu.unlp.info.tesis.database.BDUtils;

public class QueryOperator {

	private List<OperatorRestriction> restrictionList = new Vector<OperatorRestriction>();

	private DisjuntionRestriction currentDisjuntion = null;

	private ConjuntionRestriction currentConjuntion = null;

	private List<Projection> projections = new Vector<Projection>();

	private List<AliasCriteria> aliasList = new Vector<AliasCriteria>();

	// ultimo operador de restriccion ingresado
	public OperatorRestriction lastOperatorRestriction;

	public int valor = 1;

	private HashMap<String, QueryOperator> subsQuerys = new HashMap<String, QueryOperator>();

	// esta es una lista con los operadores temporales, se deben limpiar estas
	// restriciones cada
	// vez que se realiza una nueva busqueda
	private List<OperatorRestriction> temporalSearchOperators = new Vector<OperatorRestriction>();

	{
		this.createDisjuntion();
	}

	public QueryOperator() {
		super();
	}

	public Criteria createQuery(Class clase) throws HibernateException {
		Criteria criteria = null;
		criteria = BDUtils.getSession().createCriteria(clase);
		this.addRestriction(criteria);
		return criteria;
	}

	public Criteria createQuery(String className) throws HibernateException {
		Criteria criteria = null;
		criteria = BDUtils.getSession().createCriteria(className);
		this.addRestriction(criteria);
		return criteria;
	}

	private Criteria createSubQuery(Criteria criteria, String field)
			throws HibernateException {
		Criteria subCriteria = criteria.createCriteria(field);
		this.addRestriction(subCriteria);
		return subCriteria;
	}

	protected Criteria addRestriction(Criteria criteria)
			throws HibernateException {

		for (AliasCriteria alias : aliasList) {
			alias.setCriteria(criteria);
		}
		for (OperatorRestriction restriction : restrictionList) {
			criteria.add(restriction.getCriteriaExpresion());
		}
		for (OperatorRestriction restriction : temporalSearchOperators) {
			criteria.add(restriction.getCriteriaExpresion());
		}
		if (projections.size() > 0) {
			ProjectionList pl = Projections.projectionList();
			for (Projection projection : projections) {
				pl.add(projection);

			}
			criteria.setProjection(pl);
		}
		Set<String> alias = subsQuerys.keySet();
		for (String keyAlias : alias) {
			QueryOperator subQueryOperator = subsQuerys.get(keyAlias);
			subQueryOperator.createSubQuery(criteria, keyAlias);
		}
		return criteria;
	}

	public DisjuntionRestriction createDisjuntion() {
		DisjuntionRestriction dDao = new DisjuntionRestriction();
		currentDisjuntion = dDao;
		restrictionList.add(dDao);
		return dDao;
	}

	public ConjuntionRestriction createConjunction() {
		ConjuntionRestriction cDao = new ConjuntionRestriction();
		currentDisjuntion.addRestriction(cDao);
		currentConjuntion = cDao;
		return cDao;

	}

	public void closeConjunction() {
		currentConjuntion = null;

	}

	public DisjuntionRestriction createTemporalDisjuntion() {
		DisjuntionRestriction dDao = new DisjuntionRestriction();
		this.currentDisjuntion = dDao;
		temporalSearchOperators.add(dDao);
		return dDao;
	}

	public void addRestriction(OperatorRestriction restriction,
			boolean currentDisjuntion) {
		this.lastOperatorRestriction = restriction;
		// verifico si existe conjuncion abierta, siempre q haya una abierta se
		// agrega a la misma
		if (this.getCurrentConjuntion() != null) {
			this.getCurrentConjuntion().addRestriction(restriction);
			return;
		}

		if (currentDisjuntion)
			this.getCurrentDisjuntion().addRestriction(restriction);
		else
			this.restrictionList.add(restriction);

	}

	public void addTemporalRestriction(OperatorRestriction restriction,
			boolean currentDisjuntion) {
		// this.lastOperatorRestriction = restriction;
		if (currentDisjuntion)
			this.getCurrentDisjuntion().addRestriction(restriction);
		else
			this.temporalSearchOperators.add(restriction);

	}

	public void createProjGroupProperty(String field, String alias) {

		Projection proj = Projections.groupProperty(field).as(alias);

		this.projections.add(proj);

	}

	public void createProjCount(String field, String alias) {
		Projection proj = Projections.count(field).as(alias);
		this.projections.add(proj);

	}

	public void createProjCountDistinct(String field, String alias) {
		Projection proj = Projections.countDistinct(field).as(alias);
		this.projections.add(proj);

	}

	public void createProjMax(String field, String alias) {
		Projection proj = Projections.max(field).as(alias);
		this.projections.add(proj);

	}

	public void createProjMin(String field, String alias) {
		Projection proj = Projections.min(field).as(alias);
		this.projections.add(proj);
	}

	public void createProjSum(String field, String alias) {
		Projection proj = Projections.sum(field).as(alias);
		this.projections.add(proj);
	}

	public void createProjProperty(String field, String alias) {
		Projection proj = Projections.property(field).as(alias);
		this.projections.add(proj);

	}

	public void createProjSQL(String sql, String[] alias, Type[] types) {
		Projection proj = Projections.sqlProjection(sql, alias, types);
		this.projections.add(proj);

	}

	public void cleanTemporalRestriction() {
		this.temporalSearchOperators = new Vector();
	}

	public DisjuntionRestriction getCurrentDisjuntion() {
		return currentDisjuntion;
	}

	public void setCurrentDisjuntion(DisjuntionRestriction currentDisjuntion) {
		this.currentDisjuntion = currentDisjuntion;
	}

	public List<Projection> getProjections() {
		return projections;
	}

	public void setProjections(List<Projection> projections) {
		this.projections = projections;
	}

	public List<OperatorRestriction> getRestrictionList() {
		return restrictionList;
	}

	public void setRestrictionList(List<OperatorRestriction> restrictionList) {
		this.restrictionList = restrictionList;
	}

	public HashMap<String, QueryOperator> getSubsQuerys() {
		return subsQuerys;
	}

	public void setSubsQuerys(HashMap<String, QueryOperator> subsQuerys) {
		this.subsQuerys = subsQuerys;
	}

	public List<OperatorRestriction> getTemporalSearchOperators() {
		return temporalSearchOperators;
	}

	public void setTemporalSearchOperators(
			List<OperatorRestriction> temporalSearchOperators) {
		this.temporalSearchOperators = temporalSearchOperators;
	}

	public QueryOperator getSubQueryOperator(String campo) {
		return this.subsQuerys.get(campo);
	}

	public QueryOperator addSubQueryOperator(String campo) {

		QueryOperator qo = new QueryOperator();
		this.subsQuerys.put(campo, qo);
		qo.valor = valor + 1;
		return qo;
	}

	public OperatorRestriction getLastOperatorRestriction() {
		return lastOperatorRestriction;
	}

	public void setLastOperatorRestriction(
			OperatorRestriction lastOperatorRestriction) {
		this.lastOperatorRestriction = lastOperatorRestriction;
	}

	public List<AliasCriteria> getAliasList() {
		return aliasList;
	}

	public void setAliasList(List<AliasCriteria> aliasList) {
		this.aliasList = aliasList;
	}

	public ConjuntionRestriction getCurrentConjuntion() {
		return currentConjuntion;
	}

	public void setCurrentConjuntion(ConjuntionRestriction currentConjuntion) {
		this.currentConjuntion = currentConjuntion;
	}

}
